import os, os.path, time, pickle, commands, copy
from model import *
from settings import pickled_foldername, pickled_filename, \
                     flickr, log_file_name, mirror_model_pickle_name
from utils import *
import logging
import logging.handlers

# Set up a specific logger with our desired output level
logger = logging.getLogger('flickrmirror')
logger.setLevel(logging.DEBUG)

# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(
              log_file_name, maxBytes=20000, backupCount=1)

logger.addHandler(handler)

class Authenticator:
    
    token = None #authentication token
    perms = 'read' #desired permissions
    
    def __init__(self):
        pass
    
    def get_authenticated_perms(self):
        return flickr.auth_checkToken(
            auth_token=self.token.find('token').text
        ).getchildren()[0].find('perms').text
        
    def authenticated(self):
        if self.token is None:
            return False
        #elif self.get_authenticated_perms() != self.perms:
        #    return False
        else:
            return True
            
    def start_authentication(self):
        logger.info("authentication requested")
        frob = flickr.auth_getFrob().getchildren()[0].text
        commands.getoutput("""open "%s" """ % flickr.auth_url(self.perms, frob))
        return frob
        
    def end_authentication(self, frob):
        self.token = flickr.auth_getToken(frob=frob).getchildren()[0]
        flickr.token_cache.token = self.token.find("token").text
        logger.info("authentication successful")
        
    def get_userid(self):
        return self.token.getchildren()[0].find("user").get('nsid')
      
def get_directory(view, auto_create=False):
    while True:
        try:
            res = view.prompt("Type the full path of the backup directory: ")
            return Directory(res, auto_create)
        except:
            pass
              
class Directory:
    
    def __init__(self, path, auto_create = True):
        self.path = path
        
        if auto_create and not os.path.exists(path):
            os.makedirs(path)
        elif not os.path.exists(path):
            raise Exception("Folder not found")
        
    def __str__(self):
        return self.path
        
class Retriever(object):

    def __init__(self, status_method=lambda x: None):
        self.status_method = status_method

    def fill_set_with_photos(self, aset):
        """
        This method fills a set with it's photos
        """
        page = 1

        while len(aset.photos) < aset.number_of_photos_and_videos:
            aset.add_photos(flickr.photosets_getPhotos(
                photoset_id=aset.id,
                per_page=500,
                extras="original_format,last_update,media",
                page=page,
                media='all'
                ).find('photoset')
            )
            page += 1

        return aset
        
    def get_all_sets(self, filled=False):
        self.status_method("Getting photo sets.")
        sets = [Set(node) for node in flickr.photosets_getList().find('photosets').getchildren()]
        logger.info("retrieved all photo sets")
                
        if filled:
            
            set_count = 1
            
            for a_set in sets:
                self.status_method("%s/%s - Getting photo information for %s" % 
                    (set_count, len(sets), a_set.title)
                )
                self.fill_set_with_photos(a_set)
                
                set_count += 1
                
        return sets
        
    def url(self, photo):
        
        if photo.media == 'video':
            return "http://www.flickr.com/photos/%s/%s/play/orig/%s" % (
                photo.owner,
                photo.id,
                photo.originalsecret
            )
        else:
            return "http://farm%s.static.flickr.com/%s/%s_%s%s.%s" % (
                photo.farm, 
                photo.server, 
                photo.id,
                photo.originalsecret,
                '_o',
                photo.originalformat
            )
            
    def get_pickled_mirror(self, directory):
        pickler = Pickler(directory, self.status_method)
        
        if pickler.has_pickle_settings():
            self.status_method("Flickr settings found. Loading....")
            
            return pickler.load_pickle()
        else:
            return pickler.start_new()

class Pickler(object):
    
    def __init__(self, directory, status_method):
        self.status_method = status_method
        self.directory = directory
        self.pickle_path = os.path.join(directory, pickled_foldername)
        self.mirror_file_name = os.path.join(self.pickle_path, mirror_model_pickle_name)

    def has_pickle_settings(self):
        return os.path.exists(self.pickle_path) and os.path.exists(self.mirror_file_name)
        
    def load_pickle(self):
        logger.info("loading mirror model from %s" % self.mirror_file_name)
        mirror = MirrorModel().load(eval(open(self.mirror_file_name, 'rb').read()))
    
        files = [f for f in os.listdir(self.pickle_path) if f != mirror_model_pickle_name and f.endswith('.dict')]
        for fi in files:
            file_path = os.path.join(self.pickle_path, fi)
            if os.path.isfile(file_path):
                logger.info("loading set from %s" % file_path)
                fileobject = open(file_path, 'rb')
                logger.info("loaded file")
                value = eval(fileobject.read())
                logger.info("converted to value")
                s = Set().load(value)
                logger.info("created Set")
                self.status_method("Loaded set %s information from file." % (s.title))
                mirror.sets.append(s)
                
        return mirror

    def start_new(self):
        mirror = MirrorModel(Retriever(self.status_method).get_all_sets(filled=True))
        mirror.last_updated = int(time.time())
        
        #save it right away since it is the first time loading it...
        self.save_pickle(mirror)
        
        return mirror
        
    def save_pickle(self, mirror):
        logger.info("starting pickling of mirror objects")
        
        if not os.path.exists(self.pickle_path):
            os.mkdir(self.pickle_path)
        
        # save main mirror object
        logger.info("storing main mirror object")
        open(self.mirror_file_name, 'wb').write(str(mirror.dump()))
        
        logger.info("going through sets and seeing if they need to be saved.")
        for set in mirror.sets:
            if set.dirty:
                set.dirty = False
                set_filename = os.path.join(self.pickle_path, normalize(set.title) + '.dict')
                self.status_method("Saving set %s information..." % set.title)
                set.file_saved_location = set_filename
                open(set_filename, 'wb').write(str(set.dump()))
        
        return self.pickle_path

class Storer(object):
    
    def pickle_mirror(self, mirror, directory, status_method):
        return Pickler(directory, status_method).save_pickle(mirror)
    